package optimizers.ganeat.amplify;

import java.util.ArrayList;
import java.util.Map;
import java.util.Random;

import javax.swing.JPanel;

import model.Input;
import model.OligoSystemComplex;
import model.OligoSystemGeneral;
import fitness.AmplyFitness;
import fitness.SquareFitness;

import optimizers.commons.RunConfig;
import optimizers.ganeat.Connection;
import optimizers.ganeat.Node;
import optimizers.ganeat.jankenpon.ConstantsJankenpon;
import optimizers.ganeat.sin.ConstantsSin;
import optimizers.ganeat.square.IndividualSquare;
import util.MyEventHandlerAmplify;
import util.MyStepHandlerAmplify;
import util.MyUtil;
import visualization.PlotExpData;

public class AmplifyIndividual extends IndividualSquare {

	public AmplifyIndividual(Map<String, Integer> innovations,
			Map<String, String> nodeNames, int geneIndex, int indivIndex,
			RunConfig config) {
		super(innovations, nodeNames, geneIndex, indivIndex, config);
		minimize = false;
		// TODO Auto-generated constructor stub
	}

	public AmplifyIndividual(Map<String, Integer> innovations,
			Map<String, String> nodeNames, int geneIndex, int indivIndex,
			RunConfig config, String string) {
		super(innovations, nodeNames, geneIndex, indivIndex, config, string);
		minimize = false;
	}

	@Override
	public String mutateAddActivation() {
		ArrayList<Connection> possibleActivations = new ArrayList<Connection>();
		for (Node from : nodeGenes) {
			if (from.type != Node.INHIBITING_SEQUENCE) {
				for (Node to : nodeGenes) {
					if (!to.name.equals("a")
							&& getConnection(from.name, to.name) == null) {
						possibleActivations.add(new Connection(0, from, to,
								true, 0));
					}
				}
			}
		}
		if (possibleActivations.size() == 0) {
			mutationInfo = "Cannot add activation";
		} else {
			int index = rand.nextInt(possibleActivations.size());
			Connection connection = possibleActivations.get(index);
			addConnection(connection.from, connection.to, true,
					config.template.defaultValue);
			mutationInfo = "Mutate Add Activation: " + connection.from.name
					+ "->" + connection.to.name;
		}
		return mutationInfo;
	}

	@Override
	public String mutateAddInhibition(Node nodeFrom, Node nodeTo,
			double parameter) {
		if (nodeTo.name.equals("a")) {
			return "Cannot add inhibition";
		}
		ArrayList<Connection> possibleConnections = new ArrayList<Connection>();
		for (Connection conn : connectionGenes) {
			if (conn.to == nodeTo && conn.enabled) {
				possibleConnections.add(conn);
			}
		}
		Connection conn = null;
		if (possibleConnections.size() == 0) {
			conn = addConnection(nodeTo, nodeTo, true,
					config.template.defaultValue);
		} else {
			conn = possibleConnections.get(rand.nextInt(possibleConnections
					.size()));
		}
		Node inhibitionNode = new Node("I" + conn.from.name + conn.to.name,
				config.inhK.defaultValue, config.seqC.defaultValue,
				Node.INHIBITING_SEQUENCE, null);
		if (getNode(inhibitionNode.name) == null) {
			nodeGenes.add(inhibitionNode);
		} else {
			inhibitionNode = getNode(inhibitionNode.name);
		}
		addConnection(nodeFrom, inhibitionNode, true, parameter);
		mutationInfo = "Mutate Add Inhibition: " + nodeFrom.name + "->"
				+ inhibitionNode.name;

		return mutationInfo;

	}

	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;

	@Override
	public JPanel displayTimeSereis() {
		JPanel result = new JPanel();
		OligoSystemGeneral oligoModel = getOligoModel();
		// remove any connection to a
		for (int i = 0; i < oligoModel.nSimpleSequences; i++) {
			oligoModel.templates[0][i][0] = 0;
		}
		int input = 0;
		int output = 1;
		if (oligoModel.nameToInt.get("a") != null) {
			input = oligoModel.nameToInt.get("a");
		}
		if (oligoModel.nameToInt.get("b") != null) {
			output = oligoModel.nameToInt.get("b");
		}
		OligoSystemComplex oligo = new OligoSystemComplex(
				oligoModel.nSimpleSequences, oligoModel.templates,
				oligoModel.seqK, oligoModel.inhK, oligoModel.seqConcentration,
				null, null, null);
		oligo.inputs = new Input[] { new Input(input, oligo.sequences.get(0)
				.get(input), ConstantsAmply.amplitudeInput,
				ConstantsAmply.minDowntime
						+ new Random().nextInt(ConstantsAmply.maxDowntime
								- ConstantsAmply.minDowntime)) };
		oligo.sequences.get(0).get(input).fakeProtected = true;
		MyEventHandlerAmplify myEventHandler = new MyEventHandlerAmplify(
				oligo.inputs);
		MyStepHandlerAmplify myStepHandler = new MyStepHandlerAmplify(
				oligoModel.nSimpleSequences, myEventHandler);
		myStepHandler.inputs = oligo.inputs;
		double[][] data1 = oligo.calculateTimeSeries(myStepHandler,
				myEventHandler);
		int firstPeak = MyUtil.getFirstPeak(data1[output],
				ConstantsSin.skipTime);

		String[] seqNames = new String[data1.length + 2];
		for (int i = 0; i < data1.length; i++) {
			seqNames[i] = oligoModel.intToName.get(i);
			if (seqNames[i] == null)
				seqNames[i] = String.valueOf(i);
		}
		seqNames[data1.length] = getGoalStringName();
		seqNames[data1.length + 1] = "sin";
		final PlotExpData demo1 = new PlotExpData("Generation "
				+ this.generationIndex + " Index " + this.individualIndex,
				data1, seqNames, "Time", "Expression");
		result.add(demo1);
		return (result);
	}

}
